//
// Created by mio on 25-4-10.
//

#include "BufferWriter.h"
#include "TcpSocket.h"

#include <cstring>
#include <errno.h>



BufferWriter::BufferWriter(int capacity) : max_queue_length_(capacity) {}

bool BufferWriter::Append(std::shared_ptr<char> data, uint32_t size, uint32_t index)
{
    // 基本参数检查：发送起始位置不能超过总大小
    if (size < index)
        return false;
    // 检查队列是否已满
    if (buffer_.size() >= max_queue_length_)
        return false;
    Packet pkt = { data, size, index };
    buffer_.emplace(std::move(pkt));
    return true;
}

bool BufferWriter::Append(const char* data, uint32_t size, uint32_t index)
{
    if (size <= index)
        return false;
    if (buffer_.size() >= max_queue_length_)
        return false;
    Packet pkt;
    // --- 内存分配与拷贝 ---
    // 1. 为数据创建副本：分配一个新的 char 数组。
    //    注意：分配 size + 512 字节可能比实际需要的 size 要多，
    //    这可能是为了预留空间或其他目的，但也可能造成浪费或潜在问题。
    //    如果只是为了存储 data，分配 size 字节通常足够。
    // 2. 使用 shared_ptr 管理新分配的内存，并指定正确的删除器 (std::default_delete<char[]>())。
    pkt.data.reset(new char[size + 512], std::default_delete<char[]>());
    // 3. 将传入的 data 内容拷贝到新分配的内存中。
    memcpy(pkt.data.get(), data, size);
    pkt.size = size;        // 数据总大小
    pkt.writeIndex = index; // 已写入（待发送）的起始位置
    buffer_.emplace(std::move(pkt));
    return true;
}

int BufferWriter::Send(int sockfd)
{
    int ret = 0;    // 记录最后一次 send 系统调用的返回值
    int count = 1;  // 控制循环的计数器，设计意图似乎是至少尝试发送一次队首数据
    do
    {
        if (buffer_.empty())
        {
            return 0;
        }

        count -= 1; // 消耗一次尝试机会
        Packet &pkt = buffer_.front();
        int ret = ::send(sockfd, pkt.data.get() + pkt.writeIndex, pkt.size - pkt.writeIndex, 0);
        if (ret > 0) // 成功发送了 ret 字节
        {
            pkt.writeIndex += ret; // 更新已发送位置索引
            // 检查当前 Packet 是否已全部发送完毕
            if (pkt.size == pkt.writeIndex)
            {
                // 如果发送完毕，则将该 Packet 从队列中移除
                buffer_.pop();
                // 关键：增加 count，使得循环 *可能* 继续处理下一个 Packet
                // 这允许在一个 Send 调用中发送完一个完整包后，紧接着尝试发送下一个包的部分或全部
                count += 1;
                // 如果 Packet 未发送完，count 保持为 0 或负数，循环将在本次迭代后结束
            }
        }
        else if (ret < 0)
        {
            // // 检查是否是由于发送缓冲区满 (EAGAIN/EWOULDBLOCK) 或被信号中断 (EINTR)
            // // 这在非阻塞模式下是正常情况，不应视为致命错误
            // if (errno == EAGAIN || errno == EINTR)
            // {
            //     ret = 0; // 将返回值修正为 0，表示“本次尝试未发送成功，但不是错误
            // }

            int error = WSAGetLastError();
            if (error == WSAEWOULDBLOCK || error == WSAEINPROGRESS || error == 0) {
                ret = 0;
            }
        }
    } while (count > 0);
    return ret;
    // 但当前实现返回 last_ret，调用者需要根据其值判断状态（>0 成功, 0 需重试, <0 错误）
}

void WriteUint32BE(char* p, uint32_t value)
{
    /* 0x12 34 56 78
     *  value >> 24 -> 0x00 00 00 12 -> p[0] = 0x12
     *  value >> 16 -> 0x00 00 12 34 -> p[1] = 0x34
     *  value >> 8  -> 0x00 12 34 56 -> p[2] = 0x56
     *  value & 0xff -> 0x12 34 56 78 & 0x 00 00 00 ff = 0x00 00 00 78 -> p[3] = 0x78
     */
    p[0] = value >> 24;
    p[1] = value >> 16;
    p[2] = value >> 8;
    p[3] = value & 0xff;
}

void WriteUint32LE(char* p, uint32_t value)
{
    /* 0x12 34 56 78
     *  value & 0xff -> 0x12 34 56 78 & 0x 00 00 00 ff = 0x00 00 00 78 -> p[0] = 0x78
     *  value >> 8  -> 0x 00 12 34 56 -> p[1] = 56
     *  value >> 16 -> 0x 00 00 12 34 -> p[2] = 34
     *  value >> 24 -> 0x 00 00 00 12 -> p[3] = 12
     */
    p[0] = value & 0xff;
    p[1] = value >> 8;
    p[2] = value >> 16;
    p[3] = value >> 24;
}

void WriteUint24BE(char* p, uint32_t value)
{
    p[0] = value >> 16;
    p[1] = value >> 8;
    p[2] = value & 0xff;
}

void WriteUint24LE(char* p, uint32_t value)
{
    /* 0x12 34 56 78
     *  value & 0xff -> 0x12 34 56 78 & 0x 00 00 00 ff = 0x00 00 00 78 -> p[0] = 0x78
     *  value >> 8  -> 0x 00 12 34 56 -> p[1] = 56
     *  value >> 16 -> 0x 00 00 12 34 -> p[2] = 34
     */
    p[0] = value & 0xff;
    p[1] = value >> 8;
    p[2] = value >> 16;
}

void WriteUint16BE(char* p, uint32_t value)
{
    p[0] = value >> 8;
    p[1] = value & 0xff;
}

void WriteUint16LE(char* p, uint32_t value)
{
    p[0] = value & 0xff;
    p[1] = value >> 8;
}
